package com.module.ble.widget

import android.animation.ValueAnimator
import android.annotation.SuppressLint
import android.content.Context
import android.graphics.Canvas
import android.graphics.DashPathEffect
import android.graphics.LinearGradient
import android.graphics.Paint
import android.graphics.Path
import android.graphics.Point
import android.graphics.RectF
import android.graphics.Shader
import android.graphics.Typeface
import android.util.AttributeSet
import android.view.MotionEvent
import android.view.View
import android.view.ViewConfiguration
import android.view.animation.DecelerateInterpolator
import com.common.base.app.extras.dp2px
import com.common.base.utils.ViewUtils.getColor
import com.module.ble.R
import com.module.ble.data.bean.CusChartData
import kotlin.math.abs
import kotlin.math.roundToInt

/**
 *
 * 作者：sosou
 *
 * 版本：1.0
 *
 * 创建日期：2024/10/24
 *
 * 描述：血氧柱状日图表
 *
 * 修订历史：
 *
 */
class BloodOxygenChart(context: Context, attrs: AttributeSet?=null) : View(context, attrs) {

    private var onDaySelectListener: ((time: Long, heart: Float) -> Unit)? = null
    private var onDayMoveListener: ((isPre: Boolean) -> Unit)? = null

    //屏幕宽高
    private var scrWidth = 0f
    private var scrHeight = 0f
    private var xData: Array<String> = arrayOf("00", "06", "12", "18", "24")
    private var yData: Array<Int> = arrayOf(100, 90, 80, 70, 0)
    private var heartData = mutableListOf<CusChartData>()
    private lateinit var paintLine: Paint //y轴线
    private lateinit var zeroPaintLine: Paint //y轴0线
    private lateinit var paintGradientLine: Paint //指示渐变竖线
    private lateinit var paintXText: Paint //x轴坐标
    private lateinit var paintYText: Paint //y轴坐标
    private lateinit var paintRound: Paint  //指示滑块圆
    private lateinit var paintBessel: Paint  //滑块底部

    private var xSlider = 0f //滑块的x轴位置

    private var mPath: Path  //滑块贝塞尔

    //第一条曲线的坐标
    private val mFirstCurveStartPoint = Point()
    private val mFirstCurveEndPoint = Point()
    private val mFirstCurveControlPoint1 = Point()
    private val mFirstCurveControlPoint2 = Point()

    //第二条曲线的坐标
    private var mSecondCurveStartPoint = Point()
    private val mSecondCurveEndPoint = Point()
    private val mSecondCurveControlPoint1 = Point()
    private val mSecondCurveControlPoint2 = Point()

    private var isRest = false //是否静息

    private val margin = 20f //左右两边距离
    private var xWithStart = 0f //x轴的起始点
    private var xWithEnd = 0f  //x轴结束点
    private var ySpacing = 0f //高度分割份数后间距
    private var xSpacing = 0f //x轴柱子分割份数后间距
    private var xTextSpacing = 0f //x轴文字分割份数后间距

    // 柱状图相关属性
    private val totalBarsPerDay = 48 // 一天48个半小时

    private val curveCircleRadius = 12f

    // 缓存相关
    private val touchSlop = ViewConfiguration.get(context).scaledTouchSlop
    private var barGradientShader: Shader? = null

    // 控制变量
    private var showZeroLine = true
    private var showSlider = false

    private var paintBarDefaultColor = getColor(R.color.c_6acd7a)
    private var zeroLineColor= getColor(R.color.c_f97975)    // 0刻度线线条颜色

    // 添加控制开关和最大值常量
    private var showBarBackground = false
    private val maxBarValue = 100 // 血氧最大值
    private val barBackgroundColor = getColor(R.color.black10) // 背景柱子颜色，可以根据需求调整

    private var showYAxisLabel = true  // 控制是否显示右边文字
    private val textLineGap = dp2px(8f)  // 文字和虚线之间的间距
    private var maxYAxisTextWidth = 0f  // 缓存最大文字宽度

    init {
        setLayerType(LAYER_TYPE_SOFTWARE, null)
        isDrawingCacheEnabled = true
        setWillNotDraw(false)
        mPath = Path()
        initPaint()
    }

    private fun initPaint() {
        paintLine = Paint().apply {
            style = Paint.Style.STROKE
            strokeWidth = 1f
            color = getColor(R.color.c_2e2e2e)
            pathEffect = DashPathEffect(floatArrayOf(12f, 6f), 0f)
        }
        zeroPaintLine= Paint().apply {
            style = Paint.Style.STROKE
            strokeWidth = 1f
            color = zeroLineColor
            // 设置虚线效果
            // 第一个参数数组表示虚线的格式：[实线长度, 空白长度]
            pathEffect = DashPathEffect(floatArrayOf(12f, 6f), 0f)
        }

        paintGradientLine = Paint().apply {
            style = Paint.Style.STROKE
            strokeWidth = 2f
        }

        paintXText = Paint().apply {
            isAntiAlias = true
            strokeWidth = 1f
            textSize = dp2px(12f)
            textAlign = Paint.Align.CENTER
            color = getColor(R.color.black)
        }

        paintYText = Paint().apply {
            isAntiAlias = true
            textSize = dp2px(11f)
            strokeWidth = 1f
            textAlign = Paint.Align.RIGHT
            color = getColor(R.color.color_808080)
        }

        paintRound = Paint().apply {
            style = Paint.Style.FILL
            isAntiAlias = true
            color = getColor(R.color.c_6acd7a)
        }

        paintBessel = Paint().apply {
            style = Paint.Style.FILL
            isAntiAlias = true
            color = getColor(R.color.color_eeeded)
        }
    }

    private fun calculateLayout() {
        // 将 onSizeChanged 中的布局计算逻辑移到这里
        ySpacing = height * 1.05f / 6f

        // 计算文字宽度并考虑是否显示Y轴标签
        val rightSpace = if (showYAxisLabel) {
            val maxYTextWidth = yData.maxOf { paintYText.measureText(it.toString()) }
            maxYTextWidth + textLineGap
        } else {
            0f
        }

        // 左侧X轴文字宽度
        val leftTextWidth = paintXText.measureText(xData[0])

        // 重新计算图表的起始和结束位置
        xWithStart = margin + leftTextWidth / 2
        xWithEnd = scrWidth - margin - rightSpace

        // 重新计算间距
        val availableWidth = xWithEnd - xWithStart
        xTextSpacing = availableWidth / (xData.size - 1)
        xSpacing = availableWidth / (totalBarsPerDay - 1)

        // 更新滑块位置，确保在有效范围内
        xSlider = xSlider.coerceIn(xWithStart, xWithEnd)
    }

    override fun onSizeChanged(w: Int, h: Int, oldw: Int, oldh: Int) {
        super.onSizeChanged(w, h, oldw, oldh)
        scrWidth = width.toFloat()
        scrHeight = height.toFloat()
        calculateLayout()
    }

    private var mDownX = 0f
    private var mDownY = 0f
    private var isSlider = false //是否开启触摸模式

    @SuppressLint("ClickableViewAccessibility")
    override fun onTouchEvent(event: MotionEvent): Boolean {
        if (!showSlider) return false
        if (heartData.isNullOrEmpty()) return true//无数据是直接透传触摸

        when (event.action) {
            MotionEvent.ACTION_DOWN -> {
                mDownX = event.x
                mDownY = event.y

                // 扩大滑块的触摸判定区域
                val bottomY = ySpacing * 5.3f
                val touchArea = RectF(
                    xSlider - curveCircleRadius * 3f,
                    bottomY - curveCircleRadius * 3f,
                    xSlider + curveCircleRadius * 3f,
                    bottomY + curveCircleRadius * 3f
                )
                isSlider = touchArea.contains(event.x, event.y)
                return true
            }

            MotionEvent.ACTION_MOVE -> {
                if (isSlider) {
                    // 限制滑动范围
                    xSlider = event.x.coerceIn(xWithStart, xWithEnd)
                    findAndNotifySelectedBar()
                    invalidate()
                    return true
                }
            }

            MotionEvent.ACTION_UP, MotionEvent.ACTION_CANCEL -> {
                if (isSlider) {
                    // 吸附到最近的数据点
                    snapToNearestBar()
                } else {
                    // 处理点击事件
                    val dx = abs(event.x - mDownX)
                    val dy = abs(event.y - mDownY)

                    if (dx < touchSlop && dy < touchSlop) {
                        // 点击事件，直接跳转到点击位置
                        xSlider = event.x.coerceIn(xWithStart, xWithEnd)
                        snapToNearestBar()
                    } else if (dx > touchSlop) {
                        // 左右滑动切换日期
                        onDayMoveListener?.invoke(event.x > mDownX)
                    }
                }
                return true
            }
        }
        return super.onTouchEvent(event)
    }

    private fun findAndNotifySelectedBar() {
        // 调整索引计算，考虑柱子中间位置
        val selectedIndex = ((xSlider - xWithStart) / xSpacing).roundToInt()
            .coerceIn(0, heartData.size - 1)

        if (selectedIndex in heartData.indices) {
            val data = heartData[selectedIndex]
            onDaySelectListener?.invoke(data.createdTime, data.mValue)  // 修正缩进
        }
    }


    private fun getBarXPosition(index: Int): Float {
        return xWithStart + (index * xSpacing)
    }

    private fun snapToNearestBar() {
        // 计算当前位置对应的索引
        val selectedIndex = ((xSlider - xWithStart) / xSpacing).roundToInt()
            .coerceIn(0, heartData.size - 1)

        var nearestIndex = selectedIndex
        var minDistance = Int.MAX_VALUE
        val searchRange = 1//搜索相近的N个数据点进行自动吸附

        for (i in maxOf(0, selectedIndex - searchRange)..minOf(heartData.size - 1, selectedIndex + searchRange)) {
            if (heartData[i].mValue > 0) {
                val distance = abs(i - selectedIndex)
                if (distance < minDistance) {
                    minDistance = distance
                    nearestIndex = i
                }
            }
        }

        // 使用getBarXPosition来获取正确的X坐标
        xSlider = getBarXPosition(nearestIndex)

        if (heartData[nearestIndex].mValue > 0) {
            onDaySelectListener?.invoke(
                heartData[nearestIndex].createdTime,
                heartData[nearestIndex].mValue
            )
        }

        invalidate()
    }


    override fun onDraw(canvas: Canvas) {
        super.onDraw(canvas)

        canvas.save()
        canvas.clipRect(0f, 0f, scrWidth, scrHeight)

        drawY(canvas)
        if (showSlider) drawGradientLine(canvas)
        drawPillar(canvas)
        if (showSlider) drawBessel(canvas)
        drawX(canvas)

        canvas.restore()
    }


    // 计算Y轴位置
    // 辅助函数：计算Y轴位置
    private fun calculateYPosition(value: Int): Float {
        return when {
            value >= 90 -> {
                val ratio = (value - 90f) / 10f
                ySpacing + (ySpacing * (1 - ratio))
            }
            value >= 80 -> {
                val ratio = (value - 80f) / 10f
                ySpacing * 2 + (ySpacing * (1 - ratio))
            }
            value >= 70 -> {
                val ratio = (value - 70f) / 10f
                ySpacing * 3 + (ySpacing * (1 - ratio))
            }
            value > 0 -> {
                val ratio = value / 70f
                ySpacing * 4 + (ySpacing * (1 - ratio))
            }
            else -> ySpacing * 5
        }
    }

    private fun drawPillar(canvas: Canvas) {
        heartData.forEachIndexed { index, data ->
            val x = getBarXPosition(index)
            val actualBarWidth = xSpacing * 0.8f
            val barStartX = x - actualBarWidth / 2
            val barEndX = x + actualBarWidth / 2
            val barCenterX = x
            val bottom = ySpacing * 5f

            // 首先绘制背景（如果启用）
            if (showBarBackground) {
                val backgroundY = calculateYPosition(maxBarValue)
                val backgroundPath = Path().apply {
                    addArc(
                        RectF(barStartX, backgroundY, barEndX, backgroundY + actualBarWidth),
                        180f, 180f
                    )
                    addRect(
                        RectF(barStartX, backgroundY + actualBarWidth/2, barEndX, bottom),
                        Path.Direction.CW
                    )
                }
                canvas.drawPath(backgroundPath, Paint().apply {
                    style = Paint.Style.FILL
                    isAntiAlias = true
                    color = barBackgroundColor
                })
            }

            // 然后绘制实际数据柱子
            if (data.mValue > 0) {
                val isSelected = showSlider && abs(barCenterX - xSlider) < xSpacing/4
                val y = calculateYPosition(data.mValue.toInt())

                // 绘制数据柱子
                val barPath = Path().apply {
                    addArc(
                        RectF(barStartX, y, barEndX, y + actualBarWidth),
                        180f, 180f
                    )
                    addRect(
                        RectF(barStartX, y + actualBarWidth/2, barEndX, bottom),
                        Path.Direction.CW
                    )
                }

                // 绘制主体
                val barPaint = Paint().apply {
                    style = Paint.Style.FILL
                    isAntiAlias = true
                    color = if(showSlider) if (isSelected) getColor(R.color.c_6acd7a) else getColor(R.color.c_6acd7a_30) else paintBarDefaultColor
                }
                canvas.drawPath(barPath, barPaint)

//                // 绘制渐变
//                if (!isSelected) {
//                    if (barGradientShader == null) {
//                        barGradientShader = LinearGradient(
//                            0f, y, 0f, bottom,
//                            intArrayOf(
//                                paintBarDefaultColor,
//                                getColor(R.color.transparent)
//                            ),
//                            null,
//                            Shader.TileMode.CLAMP
//                        )
//                    }
//
//                    canvas.drawPath(barPath, Paint().apply {
//                        shader = barGradientShader
//                        isAntiAlias = true
//                    })
//                }
            }
        }
    }

    private fun drawX(canvas: Canvas) {
        xData.forEachIndexed { index, s ->
            // 调整文字x坐标计算方式，确保首尾对齐
            val x = xWithStart + xTextSpacing * index

            if (showSlider) {
                // 原有的滑块模式下的文字绘制逻辑
                val dis = abs(x - xSlider)
                val besselTopY = ySpacing * 5.3f - curveCircleRadius * 1.7f
                var y = besselTopY + paintXText.textSize / 2

                if (dis < xTextSpacing / 2) {
                    paintXText.typeface = Typeface.DEFAULT_BOLD
                    val liftRatio = (1 - dis / (xTextSpacing / 2)).coerceIn(0f, 1f)
                    val liftDistance = 32f
                    y -= liftDistance * liftRatio
                } else {
                    paintXText.typeface = Typeface.DEFAULT
                }

                canvas.drawText(s, x, y, paintXText)
            } else {
                // 无滑块模式下的文字绘制逻辑
                paintXText.typeface = Typeface.DEFAULT
                // 直接在底部绘制文字，设置一个固定的底部边距
                val y = height - dp2px(10f)
                canvas.drawText(s, x, y, paintXText)
            }
        }
    }

    private fun drawY(canvas: Canvas) {
        // 先绘制刻度线，不受 showYAxisLabel 影响
        for (i in 0..4) {
            val lineY = ySpacing * (i + 1)

            if (i == 4 && !showZeroLine) continue
            val paint = if (i == 4) zeroPaintLine else paintLine // i == 4 时是0刻度线

            canvas.drawLine(
                xWithStart,
                lineY,
                xWithEnd,
                lineY,
                paint
            )
        }

        // 只有在 showYAxisLabel 为 true 时才绘制文字
        if (!showYAxisLabel) return

        val textHeight = paintYText.fontMetrics.let { it.descent - it.ascent }

        for (i in 0..4) {
            val lineY = ySpacing * (i + 1)

            if (i == 4 && !showZeroLine) continue

            val text = yData[i].toString()
            val textWidth = paintYText.measureText(text)
            val textX = scrWidth - margin - (maxYAxisTextWidth - textWidth) / 2

            canvas.drawText(
                text,
                textX,
                lineY + (textHeight / 2) - paintYText.fontMetrics.descent,
                paintYText
            )
        }
    }

    private fun drawGradientLine(canvas: Canvas) {
        if (!showSlider) return

        val startY = ySpacing
        val endY = ySpacing * 4.8f

        val gradient = LinearGradient(
            xSlider, startY, xSlider, endY,
            intArrayOf(
                getColor(R.color.transparent),
                zeroLineColor,
                getColor(R.color.transparent)
            ), null, Shader.TileMode.MIRROR
        )
        paintGradientLine.shader = gradient

        canvas.drawLine(xSlider, startY, xSlider, endY, paintGradientLine)
    }

    private fun drawBessel(canvas: Canvas) {
        // 如果不显示滑块，直接返回
        if (!showSlider) return

        //系数值增大滑块位置下移,减小则会使滑块位置上移
        val bottomY = ySpacing * 5.4f

        // 微调贝塞尔曲线参数
        val blueCircleRadius = curveCircleRadius * 1.8f
        // 减小曲线凸起高度，使文字更贴近
        val curveHeight = blueCircleRadius * 0.6f
        val curveWidth = blueCircleRadius * 3.8f

        // 调整圆点位置，减小乘数使圆点位置略微上移(直接减小circleY的值来使圆点上移10px)
        val circleY = bottomY + blueCircleRadius * 0.4f-10f

        // 第一条曲线的起始点和控制点设置
        mFirstCurveStartPoint[(xSlider - curveWidth).toInt()] = bottomY.toInt()
        mFirstCurveEndPoint[xSlider.toInt()] = (circleY - blueCircleRadius - curveHeight).toInt()
        mSecondCurveStartPoint = mFirstCurveEndPoint
        mSecondCurveEndPoint[(xSlider + curveWidth).toInt()] = bottomY.toInt()

        // 控制点调整，使曲线更平滑
        mFirstCurveControlPoint1[(mFirstCurveStartPoint.x + curveWidth * 0.4f).toInt()] =
            mFirstCurveStartPoint.y
        mFirstCurveControlPoint2[(mFirstCurveEndPoint.x - curveWidth * 0.4f).toInt()] =
            mFirstCurveEndPoint.y

        mSecondCurveControlPoint1[(mSecondCurveStartPoint.x + curveWidth * 0.4f).toInt()] =
            mSecondCurveStartPoint.y
        mSecondCurveControlPoint2[(mSecondCurveEndPoint.x - curveWidth * 0.4f).toInt()] =
            mSecondCurveEndPoint.y

        // 绘制贝塞尔曲线路径
        mPath.reset()
        mPath.moveTo(0f, bottomY)
        mPath.lineTo(mFirstCurveStartPoint.x.toFloat(), mFirstCurveStartPoint.y.toFloat())
        mPath.cubicTo(
            mFirstCurveControlPoint1.x.toFloat(), mFirstCurveControlPoint1.y.toFloat(),
            mFirstCurveControlPoint2.x.toFloat(), mFirstCurveControlPoint2.y.toFloat(),
            mFirstCurveEndPoint.x.toFloat(), mFirstCurveEndPoint.y.toFloat()
        )
        mPath.cubicTo(
            mSecondCurveControlPoint1.x.toFloat(), mSecondCurveControlPoint1.y.toFloat(),
            mSecondCurveControlPoint2.x.toFloat(), mSecondCurveControlPoint2.y.toFloat(),
            mSecondCurveEndPoint.x.toFloat(), mSecondCurveEndPoint.y.toFloat()
        )
        mPath.lineTo(scrWidth, bottomY)
        mPath.lineTo(scrWidth, scrHeight)
        mPath.lineTo(0f, scrHeight)
        mPath.close()

        // 绘制路径和圆点
        canvas.drawPath(mPath, paintBessel)
        canvas.drawCircle(xSlider, circleY, blueCircleRadius, paintRound)
    }

    fun setValue(value: List<CusChartData>, resetPosition: Boolean = true): BloodOxygenChart {
        heartData.clear()
        heartData = value.take(totalBarsPerDay).toMutableList()

        if (resetPosition) {
            // 将滑块位置重置到起始位置（00点）
            xSlider = xWithStart

            // 寻找第一个有效数据并通知监听器
            val firstValidIndex = heartData.indexOfFirst { it.mValue > 0 }
            if (firstValidIndex != -1) {
                val data = heartData[firstValidIndex]
                onDaySelectListener?.invoke(data.createdTime, data.mValue)
            }
        }

        barGradientShader = null
        if (width > 0 && height > 0) {
            calculateLayout()
            invalidate()
        }
        return this
    }
    // 添加动画效果
    private fun animateSliderTo(targetX: Float) {
        ValueAnimator.ofFloat(xSlider, targetX).apply {
            duration = 300
            interpolator = DecelerateInterpolator()
            addUpdateListener { animator ->
                xSlider = animator.animatedValue as Float
                invalidate()
            }
            start()
        }
    }

    fun setOnDaySelectListener(l: ((time: Long, heart: Float) -> Unit)): BloodOxygenChart {
        this.onDaySelectListener = l
        return this
    }

    fun setRest(isRest: Boolean) {
        this.isRest = isRest
        invalidate()
    }

    fun setShowZeroLine(show: Boolean): BloodOxygenChart {
        this.showZeroLine = show
        invalidate()
        return this
    }

    fun setShowZeroLineColor(mColor: Int): BloodOxygenChart {
        this.zeroLineColor = mColor
        this.zeroPaintLine.color = zeroLineColor
        this.paintRound.color = zeroLineColor
        invalidate()
        return this
    }

    /**
     * 设置是否显示底部滑块
     * @param show Boolean true显示滑块和指示器，false只显示时间文字
     */
    fun setShowSlider(show: Boolean): BloodOxygenChart {
        this.showSlider = show
        this.paintBarDefaultColor = if(show)getColor(R.color.c_6acd7a_30) else getColor(R.color.c_6acd7a)
        invalidate()
        return this
    }

    // 添加设置方法
    fun setShowBarBackground(show: Boolean): BloodOxygenChart {
        this.showBarBackground = show
        invalidate()
        return this
    }

    fun setShowYAxisLabel(show: Boolean): BloodOxygenChart {
        this.showYAxisLabel = show
        invalidate()  // 需要重新布局因为会影响图表宽度
        return this
    }

}